<template>
  <div>
    <zlw-nav></zlw-nav>
    <el-card class="box-card" style="margin-top: 70px">
      <div slot="header">
        <div class="card-header">
          <p style="color: #42b983;text-decoration: none;text-align: center;font-size: 20px">
            基于gpt-4o模型的多功能聊天机器人</p>
        </div>
      </div>

      <div class="chat-box">
        <!-- 聊天记录区域 -->
        <div class="chat-record" ref="chatRecord">
          <div class="chat-item" v-for="(item,index) in chatList">
            <div class="avatar-container" v-if="!item.isSelf">
              <img class="avatar" :src="item.avatar" alt="">
            </div>
            <div class="chat-content-container">
              <div class="nickname-time-container">
                <div class="nickname" v-if="!item.isSelf">{{ item.nickname }}</div>
                <div class="time">{{ item.time }}</div>
                <div class="nickname" v-if="item.isSelf">{{ item.nickname }}</div>
              </div>
              <div class="chat-content-wrapper">
                <div class="chat-content" v-if="item.type === 'text'" v-html="item.content"></div>
                <div class="chat-content" v-else-if="item.type === 'image'">
                  <img :src="item.content" alt="">
                </div>
                <el-button type="info" v-show="item.type === 'text'" plain @click="copyText(index)" v-if="item.isCopy">
                  COPY
                </el-button>
                <!--                <el-button type="info" v-show="item.type === 'image'" plain @click="downloadImage(item.content)"-->
                <!--                           v-if="item.isCopy">下载-->
                <!--                </el-button>-->
              </div>
            </div>
            <div class="avatar-container" v-if="item.isSelf">
              <img class="avatar" :src="item.avatar" alt="">
            </div>
          </div>
          <div class="el-backtop" @click="toTop" v-show="showBacktop"
               style="z-index: 999999;width:25px;height:25px;opacity:0.7;position: fixed;right: 50px;bottom: 200px">
            <svg style="width: 15px;height: 15px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
              <path fill="currentColor" d="M512 320 192 704h639.936z"></path>
            </svg>
          </div>
          <el-button type="danger" @click="clear">清空聊天内容</el-button>
        </div>
        <!-- 输入框区域 -->
        <!--    普通问答    -->
        <div v-if="messageType===0" class="chat-input">
          <div class="options">
            <el-tag class="option" @click="toImage" type="info" effect="plain">图像生成</el-tag>
            <el-tag class="option" @click="toAudio" type="info" effect="plain">语音生成</el-tag>
            <el-upload
              style="display: inline"
              :action="uploadURL"
              :headers="uploadHeaders"
              name="audio"
              :show-file-list="false"
              :on-success="handleAudioSuccess"
              :before-upload="beforeAudioUpload">
              <el-tag class="option" type="info" effect="plain">语音转文字</el-tag>
              <!--              <div slot="tip" class="el-upload__tip">点击上传头像，只能上传jpg/png文件，且不超过1mb</div>-->
            </el-upload>
            <el-upload
              style="display: inline-block"
              name="img"
              :action="uploadURLIMG"
              :file-list="fileList"
              list-type="picture"
              :on-remove="handleRemove"
              :on-success="handleImgSuccess"
              :before-upload="beforeImgUpload">
              <el-tag class="option" type="info" effect="plain">添加图片</el-tag>
            </el-upload>
          </div>
          <el-form ref="form" size="small" :model="formData" label-position="left" label-width="70px"
                   style="width: 100%;">
            <el-form-item v-if="fileList.length===0" @keyup.enter.native="sendMessage" label="消息内容">
              <el-input placeholder="请输入内容" clearable v-model="formData.message" type="textarea"
                        :autosize="{ minRows: 2, maxRows: 6 }"></el-input>
            </el-form-item>
            <el-form-item v-else @keyup.enter.native="sendMessage" label="图片解析">
              <el-input placeholder="请输入内容(如：解释这张图片)" clearable v-model="formData.message" type="textarea"
                        :autosize="{ minRows: 2, maxRows: 6 }"></el-input>
            </el-form-item>
            <el-form-item style="float: right;display: inline">


              <el-button type="info" @click="reset">重置</el-button>
              <el-button type="primary" @click="sendMessage">发送</el-button>
            </el-form-item>
          </el-form>
        </div>
        <!--   图像生成    -->
        <div v-else-if="messageType===1" class="chat-input">
          <div class="options">
            <el-tag class="option" @close="toChat" closable>图像生成</el-tag>
            <el-tag class="option" @click="toAudio" type="info" effect="plain">语音生成</el-tag>
            <el-upload
              style="display: inline"
              :action="uploadURL"
              name="audio"
              :show-file-list="false"
              :on-success="handleAudioSuccess"
              :before-upload="beforeAudioUpload">
              <el-tag class="option" type="info" effect="plain">语音转文字</el-tag>
              <!--              <div slot="tip" class="el-upload__tip">点击上传头像，只能上传jpg/png文件，且不超过1mb</div>-->
            </el-upload>
          </div>
          <el-form ref="form" size="small" :model="formData" label-position="left" label-width="70px"
                   style="width: 100%;">
            <el-form-item @keyup.enter.native="sendMessage" label="图片生成">
              <el-input placeholder="描述你所想象的画面，角色，情绪，场景，风格..." clearable v-model="formData.message"
                        type="textarea"
                        :autosize="{ minRows: 2, maxRows: 6 }"></el-input>
            </el-form-item>
            <el-form-item style="float: right">
              <el-button type="info" @click="reset">重置</el-button>
              <el-button type="primary" @click="sendMessage">发送</el-button>
            </el-form-item>
          </el-form>
        </div>
        <!--   语音生成    -->
        <div v-else-if="messageType===2" class="chat-input">
          <div class="options">
            <el-tag class="option" @click="toImage" type="info" effect="plain">图像生成</el-tag>
            <el-tag class="option" @close="toChat" closable>语音生成</el-tag>
            <el-upload
              style="display: inline"
              :action="uploadURL"
              name="audio"
              :show-file-list="false"
              :on-success="handleAudioSuccess"
              :before-upload="beforeAudioUpload">
              <el-tag class="option" type="info" effect="plain">语音转文字</el-tag>
              <!--              <div slot="tip" class="el-upload__tip">点击上传头像，只能上传jpg/png文件，且不超过1mb</div>-->
            </el-upload>
          </div>
          <el-form ref="form" size="small" :model="formData" label-position="left" label-width="70px"
                   style="width: 100%;">
            <el-form-item @keyup.enter.native="sendMessage" label="语音生成">
              <el-input placeholder="请输入你要转换成语音的文字" clearable v-model="formData.message" type="textarea"
                        :autosize="{ minRows: 2, maxRows: 6 }"></el-input>
            </el-form-item>
            <el-form-item style="float: right">
              <el-button type="info" @click="reset">重置</el-button>
              <el-button type="primary" @click="sendMessage">发送</el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </el-card>
  </div>
</template>

<script>
import MarkdownIt from "markdown-it";
import {MessageBox, Notification} from "element-ui";
import mynav from "../components/nav.vue";
import {aiAudioDownload, aiChat, aiImage, multiModel} from "../api/ai";
import store from "../store";
import {getToken} from "../utils/auth";

const md = new MarkdownIt();

export default {
  data() {
    return {
      fileList: [],
      uploadURLIMG: '',
      uploadURL: '',
      uploadHeaders: {
        token: getToken() // 假设你的 token 存储在 localStorage 中
      },
      srcList: [],
      messageType: 0,//0普通问答，1图像生成，2语音生成
      showBacktop: false,
      wrapStyle: {}, // 滚动容器的样式
      chatNickName: "小Z",
      isAnswered: false,
      loading: true,
      chatAvatar: 'https://zlw-blog.oss-cn-hangzhou.aliyuncs.com/2023/05/06/robot.JPEG',
      userAvatar: 'https://zlw-blog.oss-cn-hangzhou.aliyuncs.com/2023/05/06/7.jpg',
      userinfo: null,
      chatContent: '',
      chatList: [
        // 聊天记录列表示例
        {
          id: 2,
          nickname: '小Z',
          avatar: 'https://zlw-blog.oss-cn-hangzhou.aliyuncs.com/2023/05/06/robot.JPEG',
          type: 'text',
          content: 'Hello~我是Zlwblog技术论坛的智能技术客服小Z，在线为您提供帮助和解答技术问题！',
          time: '00:00',
          isSelf: false,
          isCopy: false
        },
        // ...
      ],
      formData: {
        message: '' // 消息内容
      }
    };
  },
  components: { //定义组件
    'zlw-nav': mynav
  },
  mounted() {
    this.uploadURLIMG = store.state.baseURL + 'upload'
    this.uploadURL = store.state.baseURL + 'audioToText'
    this.userinfo = JSON.parse(localStorage.getItem('userInfo')) || null;
    this.$nextTick(() => {
      this.setShowBacktop();
      this.$refs.chatRecord.addEventListener("scroll", this.setShowBacktop);
    });
  },
  beforeUnmount() {
    this.$refs.chatRecord.removeEventListener("scroll", this.setShowBacktop);
  },
  methods: {
    handleRemove() {
      this.fileList.pop()
    },
    //事件处理器
    handleImgSuccess(res, file) {//上传头像
      if (res.code == 200) {
        this.fileList.push(res.data);
      } else {
        this.$message.error('上传图片失败');
      }

    },
    beforeImgUpload(file) {
      const isOneImg = this.fileList.length !== 1;
      if (!isOneImg) {
        this.$message.error('只能添加一张图片进行解析');
      }
      //判断头像大小
      const isJPG = file.type == 'image/png' || file.type == 'image/jpg' || file.type == 'image/jpeg';
      const isLt2M = file.size / 1024 / 1024 < 1;
      // console.log(file);
      if (!isJPG) {
        this.$message.error('添加图片只能是 JPG/JPEG/PNG 格式!');
      }
      // if (!isLt2M) {
      //   this.$message.error('添加图片大小不能超过 1MB!');
      // }
      return isJPG && isOneImg;
    },

    //事件处理器
    handleAudioSuccess(res, file) {//上传头像
      if (res.code == 200) {
        const answerItem = {
          id: this.generateRandomId(),
          nickname: this.chatNickName,
          avatar: this.chatAvatar,
          type: 'text',
          content: "语音识别结果：" + res.data,
          copyContent: res.data,
          time: new Date().toLocaleTimeString(),
          isSelf: false,
          isCopy: false
        };
        this.chatList.push(answerItem);
        this.scrollToBottom()
      } else {
        let message = "语音上传失败，请稍后再试";
        if (res.toString().includes("Full authentication")) {
          message = "需要先登录我才能回答你的问题哦~"
        }
        const errorItem = {
          id: 2,
          nickname: this.chatNickName,
          avatar: this.chatAvatar,
          type: 'text',
          content: message,
          time: new Date().toLocaleTimeString(),
          isSelf: false,
          isCopy: false
        };
        this.chatList.push(errorItem);
        this.scrollToBottom()
      }
    },
    beforeAudioUpload(file) {
      // alert(file.type) audio/mpeg
      const isAudio = file.type == 'audio/mpeg';
      // console.log(file);
      if (!isAudio) {
        this.$message.error('上传文件只能是 mp3 格式!');
        return;
      }
      const waitItem = {
        id: 4,
        nickname: this.chatNickName,
        avatar: this.chatAvatar,
        type: 'text',
        content: "请稍等，语音正在识别中。。。",
        time: new Date().toLocaleTimeString(),
        isSelf: false,
        isCopy: false
      };
      this.chatList.push(waitItem);
      this.scrollToBottom()
    },
    downloadImage(imageUrl) {

      // 创建一个隐形的a元素
      const link = document.createElement('a');
      link.href = imageUrl;

      // 指定下载下来的文件名
      link.download = new Date().getTime() + '.jpg';

      // 将元素添加到DOM中以触发下载
      document.body.appendChild(link);

      // 触发点击事件
      link.click();

      // 然后移除该元素
      document.body.removeChild(link);
    },
    toChat() {
      this.messageType = 0
    },
    toImage() {
      this.messageType = 1
    },
    toAudio() {
      this.messageType = 2
    },
    toTop() {
      let scrollTop = this.$refs.chatRecord.scrollTop;
      if (scrollTop > 0) {
        const timeTop = setInterval(() => {
            this.$refs.chatRecord.scrollTop = scrollTop -= 50; //一次减50往上滑动
            if (scrollTop <= 0) {
              clearInterval(timeTop);
            }
          },
          10); //定时调用函数使其更顺滑
      }
    },
    setShowBacktop() {
      const scrollTop = this.$refs.chatRecord.scrollTop || this.$refs.chatRecord.pageYOffset;
      this.showBacktop = scrollTop > 0;
      console.log(this.showBacktop)
    },
    clear() {
      if (this.chatList.length === 1) {
        this.$message.success("当前没有什么需要清除的，开始你的提问吧~")
      } else {
        MessageBox.confirm(
          '确定要清空所有聊天内容?',
          'Really？',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
          }
        )
          .then(() => {
            this.chatList = [
              {
                id: 2,
                nickname: '小Z',
                avatar: 'https://zlw-blog.oss-cn-hangzhou.aliyuncs.com/2023/05/06/robot.JPEG',
                type: 'text',
                content: 'Hello~我是Zlwblog技术论坛的智能技术客服小Z，在线为您提供帮助和解答技术问题！',
                time: '00:00',
                isSelf: false,
                isCopy: false
              }
            ]
            this.$message.success("清除成功~")
          })
          .catch(() => {
            this.$message.info("取消了~")
          })
      }
    },
    reset() {
      this.formData.message = ''
    },
    copyText(index) {
      var textArea = document.createElement("textarea");
      textArea.value = this.chatList[index].copyContent;
      textArea.style.position = "fixed";
      document.body.appendChild(textArea);
      textArea.focus();
      textArea.select();
      const successful = document.execCommand("copy");
      document.body.removeChild(textArea);
      if (successful) {
        Notification.success({
          title: '复制成功',
          message: '快去粘贴吧~',
          showClose: false,
        })
        console.log('文本已复制到剪贴板');
      } else {
        Notification.error({
          title: '复制失败',
          message: '可能是浏览器不支持',
          showClose: false,
        })
      }

    },
    sendMessage() {
      if (this.formData.message === '' || this.formData.message == null) {
        this.$message.info("请输入聊天内容~")
        return
      }
      // const avatar = this.userAvatar; // 当前用户的头像
      const avatar = this.userinfo == null ? this.userAvatar : this.userinfo.avatar; // 当前用户的头像
      const nickname = this.userinfo == null ? '体验版用户' : this.userinfo.nickName; // 当前用户的昵称
      const time = new Date().toLocaleTimeString(); // 利用系统当前时间作为发送时间
      const userInput = this.fileList.length === 0 ? this.formData.message : `${this.formData.message}<br><img style="max-width: 500px;
    max-height: 500px;" src="${this.fileList[0]}" alt="">`
      const item = {
        id: 1,
        nickname,
        avatar,
        type: 'text',
        content: userInput,
        time,
        isSelf: true,
        isCopy: false
      };

      this.chatList.push(item);
      this.scrollToBottom()

      const waitItem = {
        id: 4,
        nickname: this.chatNickName,
        avatar: this.chatAvatar,
        type: 'text',
        content: "请耐心等待，机器人正在思考中...",
        time: new Date().toLocaleTimeString(),
        isSelf: false,
        isCopy: false
      };
      this.chatList.push(waitItem);
      this.scrollToBottom()
      // 调用api处理提问
      // this.startStreaming(this.formData.message)
      const message = this.formData.message;
      this.formData.message = '';
      if (this.messageType === 0) {
        //普通问答
        if (this.fileList.length === 0) {
          aiChat(message).then((res) => {
            const answerItem = {
              id: this.generateRandomId(),
              nickname: this.chatNickName,
              avatar: this.chatAvatar,
              type: 'text',
              content: md.render(res),
              // content: res,
              copyContent: res,
              time: new Date().toLocaleTimeString(),
              isSelf: false,
              isCopy: true
            };
            this.chatList.push(answerItem);
            this.scrollToBottom()
            console.log(answerItem)
          }).catch((res) => {
            let message = "很抱歉，我出现了一些小问题。请多尝试几次或者联系我的主人（499152654@qq.com）进行修复。谢谢您的耐心等待！ (●'◡'●)ﾉ♥\"";
            if (res.toString().includes("Full authentication")) {
              message = "需要先登录我才能回答你的问题哦~"
            }
            const errorItem = {
              id: 2,
              nickname: this.chatNickName,
              avatar: this.chatAvatar,
              type: 'text',
              content: message,
              time: new Date().toLocaleTimeString(),
              isSelf: false,
              isCopy: false
            };
            this.chatList.push(errorItem);
            this.scrollToBottom()
          })
        } else {
          // 多模态
          const multiModelDto = {
            "userInput": message,
            "imgUrl": this.fileList.pop()
          }
          multiModel(multiModelDto).then((res) => {
            const answerItem = {
              id: this.generateRandomId(),
              nickname: this.chatNickName,
              avatar: this.chatAvatar,
              type: 'text',
              content: md.render(res),
              copyContent: res,
              time: new Date().toLocaleTimeString(),
              isSelf: false,
              isCopy: true
            };
            this.chatList.push(answerItem);
            this.scrollToBottom()
            console.log(answerItem)
          }).catch((res) => {
            let message = "很抱歉，我出现了一些小问题。请多尝试几次或者联系我的主人（499152654@qq.com）进行修复。谢谢您的耐心等待！ (●'◡'●)ﾉ♥\"";
            if (res.toString().includes("Full authentication")) {
              message = "需要先登录我才能回答你的问题哦~"
            }
            const errorItem = {
              id: 2,
              nickname: this.chatNickName,
              avatar: this.chatAvatar,
              type: 'text',
              content: message,
              time: new Date().toLocaleTimeString(),
              isSelf: false,
              isCopy: false
            };
            this.chatList.push(errorItem);
            this.scrollToBottom()
          })
        }
      }
      //图片生成
      else if (this.messageType === 1) {
        aiImage(message).then((res) => {
          const answerItem = {
            id: this.generateRandomId(),
            nickname: this.chatNickName,
            avatar: this.chatAvatar,
            type: 'image',
            content: res,
            copyContent: res,
            time: new Date().toLocaleTimeString(),
            isSelf: false,
            isCopy: false
          };
          this.chatList.push(answerItem);
          this.scrollToBottom()
        }).catch((res) => {
          let message = "很抱歉，我出现了一些小问题。请多尝试几次或者联系我的主人（499152654@qq.com）进行修复。谢谢您的耐心等待！ (●'◡'●)ﾉ♥\"";
          if (res.toString().includes("Full authentication")) {
            message = "需要先登录我才能回答你的问题哦~"
          }
          const errorItem = {
            id: 2,
            nickname: this.chatNickName,
            avatar: this.chatAvatar,
            type: 'text',
            content: message,
            time: new Date().toLocaleTimeString(),
            isSelf: false,
            isCopy: false
          };
          this.chatList.push(errorItem);
          this.scrollToBottom()
        })
      }
      //语音生成
      else if (this.messageType === 2) {
        aiAudioDownload(message).then((res) => {
          const answerItem = {
            id: this.generateRandomId(),
            nickname: this.chatNickName,
            avatar: this.chatAvatar,
            type: 'text',
            content: "语音生成成功！正在通过浏览器下载到本地。",
            copyContent: res,
            time: new Date().toLocaleTimeString(),
            isSelf: false,
            isCopy: false
          };
          this.chatList.push(answerItem);
          this.scrollToBottom()
        }).catch((res) => {
          let message = "很抱歉，我出现了一些小问题。请多尝试几次或者联系我的主人（499152654@qq.com）进行修复。谢谢您的耐心等待！ (●'◡'●)ﾉ♥\"";
          if (res.toString().includes("Full authentication")) {
            message = "需要先登录我才能回答你的问题哦~"
          }
          const errorItem = {
            id: 2,
            nickname: this.chatNickName,
            avatar: this.chatAvatar,
            type: 'text',
            content: message,
            time: new Date().toLocaleTimeString(),
            isSelf: false,
            isCopy: false
          };
          this.chatList.push(errorItem);
          this.scrollToBottom()
        })
      }
    },
    // 将滚动条滚动到底部
    scrollToBottom() {
      let scrollRef = this.$refs.chatRecord;
      setTimeout(() => {
        // scrollRef.scrollTop = scrollRef.scrollHeight;
        scrollRef.scrollTo({top: scrollRef.scrollHeight, behavior: 'smooth'})
        console.log("scrollTop:" + scrollRef.scrollTop)
      }, 20);
    },
// 生成随机UUID
    generateRandomId() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
          v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
    }
  }
};
</script>

<style scoped>
.options {
  margin-bottom: 2px;
  margin-left: 70px;

  .option {
    cursor: url(https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/Hexo/img/pointer.cur), pointer;
  }

  .option:hover {
    background-color: rgb(236, 245, 255);
  }
}

.card-header {
  height: 30px;
}

.chat-box {
  display: flex;
  flex-direction: column;
  height: 700px;
}

.chat-record {
  flex: 1;
  height: 100%;
  overflow-y: auto;
  padding: 10px;
}

.chat-item {
  display: flex;
  align-items: flex-end;
  margin-bottom: 20px;
}

.avatar-container {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  overflow: hidden;
  margin-right: 10px;
}

.avatar {
  width: 100%;
  height: 100%;
}

.chat-content-container {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.nickname-time-container {
  display: flex;
  align-items: center;
  margin-bottom: 5px;
}

.nickname {
  font-weight: bold;
  margin-right: 10px;
}

.time {
  font-size: 12px;
  margin-right: 10px;
}

.chat-content-wrapper {
  display: flex;
  align-items: flex-end;
}

.chat-content {
  text-align: left;
  padding: 5px 10px;
  border-radius: 10px;
  word-break: break-all;
  margin-right: 10px;
  background-color: #f2f3f5;
  max-width: 95%;
  line-height: 20px;
}

.chat-content img {
  max-width: 250px;
  max-height: 250px;
  cursor: url(https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/Hexo/img/pointer.cur), pointer;;
}

.chat-content img:hover {
  opacity: 0.8;
}

.chat-input {
  padding: 10px;
}

.chat-input .el-form-item {
  margin-bottom: 10px;
}
</style>
